#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
学习
http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html
mnist（手写数字）为数据源，进阶训练版本，增加多层卷积网络

数学名词：
张量（Tensor）是一个定义在的一些向量空间和一些对偶空间的笛卡儿积上的多重线性映射。（不懂）
'''
import tensorflow as tf 
import input_data


def earsyMode(mnist,sess,y_,x):
    '''
    简单模型
    '''
    #---------------变量
    '''
    说明：
        1、变量需要通过session初始化后，才能在session中使用。这一初始化步骤为，为初始值指定具体指，本初始值指定具体指（本例当中全为零），并将其分配给每个变量，可以一次性为所有变量完成操作
    '''
    W= tf.Variable(tf.zeros([784,10]))  #权重  初始化0  784x10的矩阵（784个特征和10个输出值）
    b=tf.Variable(tf.zeros([10]))       #偏置  初始化0  10纬向量

    #---------------初始化变量
    sess.run(tf.global_variables_initializer())

    #---------------类别预测与损失函数
    y= tf.nn.softmax(tf.matmul(x,W)+b) #定义模型  回归模型 向量化后图片x和权重矩阵W相乘，加上偏置b，计算每个分类的softmax概率值
    cross_entropy= -tf.reduce_sum(y_*tf.log(y)) #损失函数  目标类别和预测类别的交叉熵 reduce_sum把minibatch每张图片的交叉熵相加，所以计算的交叉熵是整个minibatch的

    #---------------损失函数和步长
    train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)  #步长 0.01 梯度下降算法优化模型

    #---------------训练模型
    for i in range(1000):
        batch=mnist.train.next_batch(50)  #加载50个训练样本
        train_step.run(feed_dict={x:batch[0],y_:batch[1]})

    #---------------评估模型
    correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))



#---------------初始化函数
def weight_variable(shape):
    initial=tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1,shape=shape)
    return tf.Variable(initial)

#--------------卷积和池化函数
def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

def convolutionMode(mnist,sess,y_,x):
    '''
    卷积模型
    '''
    #------------第一层卷积
    W_conv1 = weight_variable([5,5,1,32])
    b_conv1 = bias_variable([32])

    x_image=tf.reshape(x,[-1,28,28,1])  #x为4D向量，第2、3对应图片的宽、高，最后一维代表图片的颜色通道数（灰度图为1，RGB彩色图为3）
    h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)  #把x_image和权值向量进行卷积，加上偏置项,再用ReLU激活函数，最后maxpooling
    h_pool1=max_pool_2x2(h_conv1) 

    #------------第二层卷积
    W_conv2=weight_variable([5,5,32,64])
    b_conv2 =bias_variable([64])

    h_conv2 =tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)

    #--------------密集连接层
    W_fc1=weight_variable([7*7*64,1024])  #图片尺寸减小到7x7，加入1024个神经元全连接层
    b_fc1 = bias_variable([1024])

    h_pool2_flat= tf.reshape(h_pool2,[-1,7*7*64])
    h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)  #将池化输出的张量reshape成一些向量，乘上权重矩阵，加上偏置，再ReLU

    #-------------Dropout 减少过拟合
    keep_prob =tf.placeholder("float")
    h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)

    #-------------输出层
    W_fc2=weight_variable([1024,10])
    b_fc2 = bias_variable([10])

    y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)

    #-------------训练和评估
    cross_entropy=-tf.reduce_sum(y_*tf.log(y_conv))
    train_step=tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction=tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
    accuracy=tf.reduce_mean(tf.cast(correct_prediction,"float"))
    sess.run(tf.global_variables_initializer())
    for i in range(200):  #API训练为20000
        batch = mnist.train.next_batch(50)
        if i%100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x:batch[0], y_: batch[1], keep_prob: 1.0})
            print("step %d, training accuracy %g"%(i, train_accuracy))
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

    print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

def main():
    mnist = input_data.read_data_sets("mnist/data/", one_hot=True) #加载数据
    sess=tf.InteractiveSession()  #通过InteractiveSession创建可以插入计算图

    '''
    占位符
    说明：
        1、x和y不是特定值，它们只是占位符
        2、placeholder的shape是可选参数，但可以让TensorFlow自动捕捉因数据纬度不一致导致的错误
    '''
    y_=tf.placeholder("float",shape=[None,10])  #y_ 二维浮点数张量，其中每一行为一个10纬的one-hot向量，表示对应某一MNIST图片的类别
    x = tf.placeholder("float",shape=[None,784])  #x  二维浮点数张量，None表示图片纬度值大小不定，作为第一个纬度值，代表batch大小，784是一个展平的MNIST图片的纬度
    # earsyMode(mnist,sess,y_,x)
    convolutionMode(mnist,sess,y_,x)

if __name__ == '__main__':
    main()

